package client;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;

import com.google.protobuf.ByteString;

import server.CommandFactories;
import server.CommandFactories.CmdType;
import util.NetworkUtil;

import network.MessageService;

import command.ICallback;
import command.IResponse;
import command.protobuf.GetUserTimelineCmdArgsProtoBuf.GetUserTimelineCmdArgs;
import command.protobuf.MessageBufProtoBuf.RpcMessage;
import config.Configure;
import config.Config.ServiceMode;

/**
 * need a mechanism of how to setup the client
 * 
 * @author xiafan
 * 
 */
public class WeiboCacheClient implements IWeiboCacheClient {
	// private static final RpcMessage.Builder messageBuilder =
	// RpcMessage.newBuilder();
	ByteArrayOutputStream buf = new ByteArrayOutputStream();
	private int messageID = 0;
	private InetAddress serverAddr;
	private short port = -1;
	private Socket sock = null;
	private DataOutputStream output = null;
	private DataInputStream input = null;

	public WeiboCacheClient(InetAddress serverAddr_, short port_) {
		this.serverAddr = serverAddr_;
		this.port = port_;
	}

	@Override
	public void connect() throws IOException {
		sock = new Socket();
		sock.setSoTimeout(5000); // sock timeout time
		sock.setTcpNoDelay(true);
		sock.setKeepAlive(true);
		sock.setSoLinger(false, 0);
		SocketAddress addr = new InetSocketAddress(serverAddr, port);
		sock.connect(addr);
		if (sock != null) {
			input = new DataInputStream(new BufferedInputStream(
					sock.getInputStream()));
			output = new DataOutputStream(new BufferedOutputStream(
					sock.getOutputStream()));
		}
	}

	@Override
	public void close() throws IOException {
		sock.close();
	}

	@Override
	public List<String> getUserHomeTimeLine(String uid, long start, long end,
			int topk) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public List<String> getUserTimeLine(String uid, long start, long end,
			int topk) {
		try {
			GetUserTimelineCmdArgs cmdArgs = GetUserTimelineCmdArgs
					.newBuilder().setUid(uid).setStart(start).setEnd(end)
					.setTopk(topk).build();
			ByteArrayOutputStream output = new ByteArrayOutputStream();
			cmdArgs.writeTo(output);
			return (List<String>) invoke(output.toByteArray(),
					CmdType.getusertimeline);
		} catch (InterruptedIOException e) {
			e.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		return null;
	}

	@Override
	public void putTweets(String uid, String tweet, long timestamp) {
		// TODO Auto-generated method stub

	}

	private Object invoke(byte[] cmd, CmdType cmdType) throws IOException {
		RpcMessage send = makeMessage(cmd, CmdType.getusertimeline);
		send.writeTo(buf);
		NetworkUtil.write(output, buf.toByteArray());
		buf.reset();
		// recieve the response

		RpcMessage get = RpcMessage.parseFrom(new ByteArrayInputStream(
				NetworkUtil.read(input)));
		cmdType = CommandFactories.getCommandTypeFromInt(get.getHeader()
				.getCmdType());
		IResponse response = CommandFactories.getCommand(cmdType)
				.createResponse();
		response.read(new ByteArrayInputStream(get.getContent()
				.toByteArray()));
		if ((get.getHeader().getFlag() & MessageService.RPC_EXCEPTION) != MessageService.RPC_EXCEPTION) {
			return response.get();
		} else {
			//it means an exception is raised
			throw new IOException((String) response.get());
		}
	}

	private RpcMessage makeMessage(byte[] cmd, CmdType cmdType) {
		// RpcMessage.Builder builder = RpcMessage.newBuilder();
		ByteString byteString = ByteString.copyFrom(cmd);
		int flag = MessageService.COMMAND_FLAG;
		flag |= MessageService.CLIENT_SERVER_COMM;

		RpcMessage.Header header = RpcMessage.Header.newBuilder()
				.setCmdType(cmdType.ordinal()).setFlag(flag).setID(messageID++)
				.build();
		RpcMessage message = RpcMessage.newBuilder().setContent(byteString)
				.setHeader(header).build();
		return message;
	}

	public static void main(String[] args) throws IOException {
		WeiboCacheClient client = new WeiboCacheClient(
				InetAddress.getByName("127.0.0.1"), (short) 10011);
		client.connect();
		List<String> tweets = client.getUserTimeLine("111", 0, 0, 0);
		if (tweets == null) {
			System.out.println("null");
			System.exit(1);
			// return;
		}

		for (String tweet : tweets) {
			System.out.println(tweet);
		}
		System.exit(0);
	}

}
